1 About

1.2 Citation

If you wish to refer to any of the material from this report please cite as:

  • Anderson, B., (2020) Air Quality in Southampton (UK): Exploring the effect of UK covid 19 lockdown on air quality , Sustainable Energy Research Group, University of Southampton: Southampton, UK.

Report circulation:

  • Public

This work is (c) 2020 the University of Southampton.

2 Introduction

sotonAirDT[, `:=`(obsDate, lubridate::date(dateTimeUTC))]
sotonAirDT[, `:=`(year, lubridate::year(dateTimeUTC))]
sotonAirDT[, `:=`(origDoW, lubridate::wday(dateTimeUTC, label = TRUE))]
sotonAirDT[, `:=`(month, lubridate::month(obsDate))]

extractDT <- sotonAirDT[!is.na(value)]  # leave out 2016 so we compare with previous 3 years

# this is such a kludge
extractDT[, `:=`(decimalDate, lubridate::decimal_date(obsDate))]  # gives year.% of year

# set to 2020 'dates'
extractDT[, `:=`(date2020, lubridate::as_date(lubridate::date_decimal(2020 + (decimalDate - 
    year))))]  # sets 'year' portion to 2020 so the lockdown annotation works
extractDT[, `:=`(day2020, lubridate::wday(date2020, label = TRUE))]  # 

# 2020 Jan 1st = Weds
dt2020 <- extractDT[year == 2020]
dt2020[, `:=`(fixedDate, obsDate)]  # no need to change
dt2020[, `:=`(fixedDoW, lubridate::wday(fixedDate, label = TRUE))]
# table(dt2020$origDoW, dt2020$fixedDoW) head(dt2020[origDoW != fixedDoW])

# shift to the closest aligning day 2019 Jan 1st = Tues
dt2019 <- extractDT[year == 2019]
dt2019[, `:=`(fixedDate, date2020 - 1)]
dt2019[, `:=`(fixedDoW, lubridate::wday(fixedDate, label = TRUE))]
# table(dt2019$origDoW, dt2019$fixedDoW) head(dt2019[origDoW != fixedDoW])

# 2018 Jan 1st = Mon
dt2018 <- extractDT[year == 2018]
dt2018[, `:=`(fixedDate, date2020 - 2)]
dt2018[, `:=`(fixedDoW, lubridate::wday(fixedDate, label = TRUE))]
# table(dt2018$origDoW, dt2018$fixedDoW) head(dt2018[origDoW != fixedDoW])

# 2017 Jan 1st = Sat
dt2017 <- extractDT[year == 2017]
dt2017[, `:=`(fixedDate, date2020 - 3)]
dt2017[, `:=`(fixedDoW, lubridate::wday(fixedDate, label = TRUE))]
# table(dt2017$origDoW, dt2017$fixedDoW) head(dt2017[origDoW != fixedDoW])

fixedDT <- rbind(dt2017, dt2018, dt2019, dt2020)  # leve out 2016 for now

fixedDT[, `:=`(fixedDate, lubridate::as_date(fixedDate))]
fixedDT[, `:=`(fixedDoW, lubridate::wday(fixedDate, label = TRUE))]


fixedDT[, `:=`(compareYear, ifelse(year == 2020, "2020", "2017-2019"))]

# these should match table(fixedDT$origDoW, fixedDT$fixedDoW)

lastSCC <- max(fixedDT[source == "southampton.my-air.uk"]$dateTimeUTC)
diffSCC <- now() - lastSCC
lastAURN <- max(fixedDT[source == "AURN"]$dateTimeUTC)
diffAURN <- now() - lastAURN

Data for Southampton downloaded from :

Southampton City Council collects various forms of air quality data at the sites shown in Table 2.1. The data is available in raw form from http://southampton.my-air.uk.

Some of these sites feed data to AURN. The data that goes via AURN is ratifified to check for outliers and instrument/measurement error. AURN data less than six months old has not undergone this process.

Latest data updated:

Table 2.1 shows the available sites and sources. Note that some of the non-AURN sites appear to have stopped updating recently. For a detailed analysis of recent missing data see Section 11.1.

fixedDT[, `:=`(site, ifelse(site == "Southampton A33", "Southampton A33 (via AURN)", site))]
fixedDT[, `:=`(site, ifelse(site == "Southampton Centre", "Southampton Centre (via AURN)", 
    site))]
t <- fixedDT[!is.na(value), .(nObs = .N, firstData = min(dateTimeUTC), latestData = max(dateTimeUTC), 
    nMeasures = uniqueN(pollutant)), keyby = .(site, source)]

kableExtra::kable(t, caption = "Sites, data source and number of valid observations. note that measures includes wind speed and direction in the AURN sourced data", 
    digits = 2) %>% kable_styling()
Table 2.1: Sites, data source and number of valid observations. note that measures includes wind speed and direction in the AURN sourced data
site source nObs firstData latestData nMeasures
Southampton - A33 Roadside (near docks, AURN site) southampton.my-air.uk 54005 2017-01-01 00:00:00 2020-04-09 19:00:00 2
Southampton - Background (near city centre, AURN site) southampton.my-air.uk 145627 2017-01-25 11:00:00 2020-04-09 19:00:00 6
Southampton - Onslow Road (near RSH) southampton.my-air.uk 54442 2017-01-01 00:00:00 2020-03-31 09:00:00 2
Southampton - Victoria Road (Woolston) southampton.my-air.uk 41298 2017-01-01 00:00:00 2020-04-01 06:00:00 2
Southampton A33 (via AURN) AURN 211294 2017-01-01 00:00:00 2020-04-09 00:00:00 8
Southampton Centre (via AURN) AURN 330863 2017-01-01 00:00:00 2020-04-09 00:00:00 13

To avoid confusion and ‘double counting’, in the remainder of the analysis we replace the Southampton AURN site data with the data for the same site sourced via AURN as shown in Table 2.2. This has the disadvantage that the data is slightly less up to date (see Table 2.1).

fixedDT <- fixedDT[!(site %like% "AURN site")]

t <- fixedDT[!is.na(value), .(nObs = .N, nPollutants = uniqueN(pollutant)), keyby = .(site, 
    source)]

kableExtra::kable(t, caption = "Sites, data source and number of valid observations", digits = 2) %>% 
    kable_styling()
Table 2.2: Sites, data source and number of valid observations
site source nObs nPollutants
Southampton - Onslow Road (near RSH) southampton.my-air.uk 54442 2
Southampton - Victoria Road (Woolston) southampton.my-air.uk 41298 2
Southampton A33 (via AURN) AURN 211294 8
Southampton Centre (via AURN) AURN 330863 13

For much more detailed analysis see a longer and very messy data report.

4 Nitrogen Dioxide (no2)

yLab <- "Nitrogen Dioxide (ug/m3)"
no2dt <- fixedDT[pollutant == "no2"]

Figure 4.1 shows the most recent hourly data.

recentDT <- no2dt[obsDate > myParams$recentCutDate]
p <- makeDotPlot(recentDT, xVar = "dateTimeUTC", yVar = "value", byVar = "site", yLab = yLab)

p <- p + scale_x_datetime(date_breaks = "2 day", date_labels = "%a %d %b") + theme(axis.text.x = element_text(angle = 90, 
    hjust = 1))

p <- p + geom_hline(yintercept = myParams$hourlyNo2Threshold_WHO) + labs(caption = paste0(myParams$lockdownCap, 
    myParams$weekendCap, "\nReference line = WHO hourly threshold"))

# final plot - adds annotations
yMin <- min(recentDT$value)
yMax <- max(recentDT$value)

p <- addLockdownDateTime(p)
addWeekendsDateTime(p) + guides(colour = guide_legend(ncol = 2))
Nitrogen Dioxide levels, Southampton (hourly, recent)

Figure 4.1: Nitrogen Dioxide levels, Southampton (hourly, recent)

Figure 4.2 shows the most recent mean daily values compared to previous years. We have shifted the dates for the comparison years to ensure that weekdays and weekends line up. Note that this plot shows daily means with no indications of variance. Visible differences are therefore purely indicative at this stage.

plotDT <- no2dt[fixedDate <= lubridate::today() & fixedDate >= myParams$comparePlotCut, .(mean = mean(value), 
    nSites = uniqueN(site)), keyby = .(fixedDate, compareYear)]

# final plot - adds annotations
yMin <- min(plotDT$mean)
yMax <- max(plotDT$mean)

p <- compareYearsPlot(plotDT, xVar = "fixedDate", colVar = "compareYear")
p <- addLockdownDate(p) + labs(caption = paste0(myParams$lockdownCap, myParams$weekendCap, 
    myParams$noThresh))

addWeekendsDate(p) + scale_x_date(date_breaks = "7 day", date_labels = "%a %d %b", date_minor_breaks = "1 day")
Nitrogen Dioxide levels, Southampton (daily mean)

Figure 4.2: Nitrogen Dioxide levels, Southampton (daily mean)

Beware seasonal trends and weather effects

5 Oxides of Nitrogen (nox)

yLab <- "Oxides of Nitrogen (ug/m3)"
noxdt <- fixedDT[pollutant == "nox"]

Figure 5.1 shows the most recent hourly data.

recentDT <- noxdt[!is.na(value) & obsDate > myParams$recentCutDate]
p <- makeDotPlot(recentDT, xVar = "dateTimeUTC", yVar = "value", byVar = "site", yLab = yLab)

p <- p + scale_x_datetime(date_breaks = "2 day", date_labels = "%a %d %b") + theme(axis.text.x = element_text(angle = 90, 
    hjust = 1)) + labs(caption = paste0(myParams$lockdownCap, myParams$weekendCap, myParams$noThresh))

# final plot - adds annotations
yMin <- min(recentDT$value)
yMax <- max(recentDT$value)

p <- addLockdownDateTime(p)

addWeekendsDateTime(p)
Oxides of nitrogen levels, Southampton (hourly, recent)

Figure 5.1: Oxides of nitrogen levels, Southampton (hourly, recent)

Figure 5.2 shows the most recent mean daily values compared to previous years.

plotDT <- noxdt[fixedDate <= lubridate::today() & fixedDate >= myParams$comparePlotCut, .(mean = mean(value), 
    nSites = uniqueN(site)), keyby = .(fixedDate, compareYear)]

# final plot - adds annotations
yMin <- min(plotDT$mean)
yMax <- max(plotDT$mean)

p <- compareYearsPlot(plotDT, xVar = "fixedDate", colVar = "compareYear")
p <- addLockdownDate(p) + labs(caption = paste0(myParams$lockdownCap, myParams$weekendCap, 
    myParams$noThresh))
addWeekendsDate(p)
Oxides of nitrogen levels, Southampton (daily mean)

Figure 5.2: Oxides of nitrogen levels, Southampton (daily mean)

6 Sulphour Dioxide

yLab <- "Sulphour Dioxide (ug/m3)"
so2dt <- fixedDT[pollutant == "so2"]

Figure 6.1 shows the most recent hourly data.

recentDT <- so2dt[!is.na(value) & obsDate > myParams$recentCutDate]
p <- makeDotPlot(recentDT, xVar = "dateTimeUTC", yVar = "value", byVar = "site", yLab = yLab)

p <- p + scale_x_datetime(date_breaks = "2 day", date_labels = "%a %d %b") + theme(axis.text.x = element_text(angle = 90, 
    hjust = 1)) + labs(caption = paste0(myParams$lockdownCap, myParams$weekendCap, myParams$noThresh))

yMax <- max(recentDT$value)
yMin <- min(recentDT$value)
p <- addLockdownDateTime(p)
addWeekendsDateTime(p)
Sulphour Dioxide levels, Southampton (hourly, recent)

Figure 6.1: Sulphour Dioxide levels, Southampton (hourly, recent)

Figure 6.2 shows the most recent mean daily values compared to previous years.

plotDT <- so2dt[fixedDate <= lubridate::today() & fixedDate >= myParams$comparePlotCut, .(mean = mean(value), 
    nSites = uniqueN(site)), keyby = .(fixedDate, compareYear)]

# final plot - adds annotations
yMin <- min(plotDT$mean)
yMax <- max(plotDT$mean)

p <- compareYearsPlot(plotDT, xVar = "fixedDate", colVar = "compareYear")
p <- addLockdownDate(p) + geom_hline(yintercept = myParams$dailySo2Threshold_WHO) + labs(caption = paste0(myParams$lockdownCap, 
    myParams$weekendCap, "\nReference line = WHO daily threshold"))
addWeekendsDate(p)
Oxides of nitrogen levels, Southampton (daily mean)

Figure 6.2: Oxides of nitrogen levels, Southampton (daily mean)

Beware seasonal trends and weather effects

7 Ozone

yLab <- "Ozone (ug/m3)"
o3dt <- fixedDT[pollutant == "o3"]

Figure 7.1 shows the most recent hourly data.

recentDT <- o3dt[!is.na(value) & obsDate > myParams$recentCutDate]
p <- makeDotPlot(recentDT, xVar = "dateTimeUTC", yVar = "value", byVar = "site", yLab = yLab)

p <- p + scale_x_datetime(date_breaks = "2 day", date_labels = "%a %d %b") + theme(axis.text.x = element_text(angle = 90, 
    hjust = 1)) + labs(caption = paste0(myParams$lockdownCap, myParams$weekendCap, myParams$noThresh))

yMax <- max(recentDT$value)
yMin <- min(recentDT$value)
p <- addLockdownDateTime(p)
addWeekendsDateTime(p)
03 levels, Southampton (hourly, recent)

Figure 7.1: 03 levels, Southampton (hourly, recent)

Figure 7.2 shows the most recent mean daily values compared to previous years.

plotDT <- o3dt[fixedDate <= lubridate::today() & fixedDate >= myParams$comparePlotCut, .(mean = mean(value), 
    nSites = uniqueN(site)), keyby = .(fixedDate, compareYear)]

# final plot - adds annotations
yMin <- min(plotDT$mean)
yMax <- max(plotDT$mean)

p <- compareYearsPlot(plotDT, xVar = "fixedDate", colVar = "compareYear")
p <- addLockdownDate(p) + geom_hline(yintercept = myParams$dailyO3Threshold_WHO) + labs(caption = paste0(myParams$lockdownCap, 
    myParams$weekendCap, "\nReference line = WHO daily threshold"))
addWeekendsDate(p)
Oxides of nitrogen levels, Southampton (daily mean)

Figure 7.2: Oxides of nitrogen levels, Southampton (daily mean)

Beware seasonal trends and weather effects

8 PM 10

yLab <- "PM 10 (ug/m3)"
pm10dt <- fixedDT[pollutant == "pm10"]

Figure 8.1 shows the most recent hourly data.

recentDT <- pm10dt[!is.na(value) & obsDate > myParams$recentCutDate]
p <- makeDotPlot(recentDT, xVar = "dateTimeUTC", yVar = "value", byVar = "site", yLab = yLab)

p <- p + scale_x_datetime(date_breaks = "2 day", date_labels = "%a %d %b") + theme(axis.text.x = element_text(angle = 90, 
    hjust = 1)) + labs(caption = paste0(myParams$lockdownCap, myParams$weekendCap, myParams$noThresh))

yMax <- max(recentDT$value)
yMin <- min(recentDT$value)
p <- addLockdownDateTime(p)
addWeekendsDateTime(p)
PM10 levels, Southampton (hourly, recent)

Figure 8.1: PM10 levels, Southampton (hourly, recent)

Figure 8.2 shows the most recent mean daily values compared to previous years.

plotDT <- pm10dt[fixedDate <= lubridate::today() & fixedDate >= myParams$comparePlotCut, 
    .(mean = mean(value), nSites = uniqueN(site)), keyby = .(fixedDate, compareYear)]

# final plot - adds annotations
yMin <- min(plotDT$mean)
yMax <- max(plotDT$mean)

p <- compareYearsPlot(plotDT, xVar = "fixedDate", colVar = "compareYear")
p <- addLockdownDate(p) + geom_hline(yintercept = myParams$dailyPm10Threshold_WHO) + labs(caption = paste0(myParams$lockdownCap, 
    myParams$weekendCap, "\nReference line = WHO daily threshold"))
addWeekendsDate(p)
Oxides of nitrogen levels, Southampton (daily mean)

Figure 8.2: Oxides of nitrogen levels, Southampton (daily mean)

Beware seasonal trends and weather effects

9 PM 2.5

yLab <- "PM 2.5 (ug/m3)"
pm25dt <- fixedDT[pollutant == "pm2.5"]

Figure 9.1 shows the most recent hourly data.

recentDT <- pm25dt[!is.na(value) & obsDate > myParams$recentCutDate]
p <- makeDotPlot(recentDT, xVar = "dateTimeUTC", yVar = "value", byVar = "site", yLab = yLab)

p <- p + scale_x_datetime(date_breaks = "2 day", date_labels = "%a %d %b") + theme(axis.text.x = element_text(angle = 90, 
    hjust = 1)) + labs(caption = paste0(myParams$lockdownCap, myParams$weekendCap, myParams$noThresh))

yMax <- max(recentDT$value)
yMin <- min(recentDT$value)
p <- addLockdownDateTime(p)
addWeekendsDateTime(p)
PM2.5 levels, Southampton (hourly, recent)

Figure 9.1: PM2.5 levels, Southampton (hourly, recent)

Figure 9.2 shows the most recent mean daily values compared to previous years.

plotDT <- pm25dt[fixedDate <= lubridate::today() & fixedDate >= myParams$comparePlotCut, 
    .(mean = mean(value), nSites = uniqueN(site)), keyby = .(fixedDate, compareYear)]

# final plot - adds annotations
yMin <- min(plotDT$mean)
yMax <- max(plotDT$mean)

p <- compareYearsPlot(plotDT, xVar = "fixedDate", colVar = "compareYear")
p <- addLockdownDate(p) + geom_hline(yintercept = myParams$dailyPm2.5Threshold_WHO) + labs(caption = paste0(myParams$lockdownCap, 
    myParams$weekendCap, "\nReference line = WHO daily threshold"))
addWeekendsDate(p)
Oxides of nitrogen levels, Southampton (daily mean)

Figure 9.2: Oxides of nitrogen levels, Southampton (daily mean)

Beware seasonal trends and weather effects

10 Save data

Save data out for predictive models.

Save long form data to data folder.

fixedDT[, `:=`(weekDay, lubridate::wday(dateTimeUTC, label = TRUE, abbr = TRUE))]
f <- paste0(here::here(), "/data/sotonExtract2017_2020_v2.csv")
data.table::fwrite(fixedDT, f)
dkUtils::gzipIt(f)

Saved data description:

skimr::skim(aurnDT)
Table 10.1: Data summary
Name aurnDT
Number of rows 885672
Number of columns 8
_______________________
Column type frequency:
character 5
Date 1
numeric 1
POSIXct 1
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
date 0 1 20 20 0 43848 0
code 0 1 4 4 0 2 0
site 0 1 15 18 0 2 0
pollutant 0 1 2 5 0 13 0
source 0 1 4 4 0 1 0

Variable type: Date

skim_variable n_missing complete_rate min max median n_unique
obsDate 0 1 2016-01-01 2020-12-31 2018-05-28 1827

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
value 238316 0.73 41.62 74.67 -9.6 3.96 12.2 37.3 1007.78 ▇▁▁▁▁

Variable type: POSIXct

skim_variable n_missing complete_rate min max median n_unique
dateTimeUTC 0 1 2016-01-01 2020-12-31 23:00:00 2018-05-28 15:00:00 43848

Saved data sites by year:

t <- table(fixedDT$site, fixedDT$year)

kableExtra::kable(t, caption = "Sites available by year") %>% kable_styling()
Table 10.2: Sites available by year
2017 2018 2019 2020
Southampton - Onslow Road (near RSH) 16871 17170 16507 3894
Southampton - Victoria Road (Woolston) 12834 15394 8940 4130
Southampton A33 (via AURN) 67278 62214 65751 16051
Southampton Centre (via AURN) 103806 100155 105871 21031

Saved pollutants by site:

t <- table(fixedDT$site, fixedDT$pollutant)

kableExtra::kable(t, caption = "Pollutants available by site") %>% kable_styling()
Table 10.3: Pollutants available by site
no no2 nox nv10 nv2.5 o3 pm10 pm2.5 so2 v10 v2.5 wd ws
Southampton - Onslow Road (near RSH) 0 27219 27223 0 0 0 0 0 0 0 0 0 0
Southampton - Victoria Road (Woolston) 0 20649 20649 0 0 0 0 0 0 0 0 0 0
Southampton A33 (via AURN) 28204 28202 28204 23227 0 0 24121 0 0 23224 0 28056 28056
Southampton Centre (via AURN) 27412 27412 27412 21105 22627 27217 24741 26263 26830 21105 22627 28056 28056

NB:

  • ws = wind speed
  • wd = wind direction
  • v* = volatiles

We have also produced wind/pollution roses for these sites.

11 Annex

11.1 Missing data

Several of these datasets suffer from missing data. This is visualised below.

# dt,xvar, yvar,fillVar, yLab
tileDT <- no2dt[dateTimeUTC > as.Date("2020-02-01")]
p <- makeTilePlot(tileDT, xVar = "dateTimeUTC", yVar = "site", fillVar = "value", yLab = yLab)

p + scale_x_datetime(date_breaks = "7 day", date_labels = "%a %d %b") + theme(axis.text.x = element_text(angle = 90, 
    hjust = 1))
Nitrogen Dioxide data availability and levels over time

Figure 11.1: Nitrogen Dioxide data availability and levels over time

# dt,xvar, yvar,fillVar, yLab
tileDT <- noxdt[dateTimeUTC > as.Date("2020-02-01")]
p <- makeTilePlot(tileDT, xVar = "dateTimeUTC", yVar = "site", fillVar = "value", yLab = yLab)

p + scale_x_datetime(date_breaks = "7 day", date_labels = "%a %d %b") + theme(axis.text.x = element_text(angle = 90, 
    hjust = 1))
Oxides of nitrogen data availability and levels over time

Figure 11.2: Oxides of nitrogen data availability and levels over time

# dt,xvar, yvar,fillVar, yLab
tileDT <- so2dt[dateTimeUTC > as.Date("2020-02-01")]
p <- makeTilePlot(tileDT, xVar = "dateTimeUTC", yVar = "site", fillVar = "value", yLab = yLab)

p + scale_x_datetime(date_breaks = "7 day", date_labels = "%a %d %b") + theme(axis.text.x = element_text(angle = 90, 
    hjust = 1))
Sulphour Dioxide data availability and levels over time

Figure 11.3: Sulphour Dioxide data availability and levels over time

tileDT <- o3dt[dateTimeUTC > as.Date("2020-02-01")]
p <- makeTilePlot(tileDT, xVar = "dateTimeUTC", yVar = "site", fillVar = "value", yLab = yLab)

p + scale_x_datetime(date_breaks = "7 day", date_labels = "%a %d %b") + theme(axis.text.x = element_text(angle = 90, 
    hjust = 1))
Availability and level of o3 data over time

Figure 11.4: Availability and level of o3 data over time

tileDT <- pm10dt[dateTimeUTC > as.Date("2020-02-01")]
p <- makeTilePlot(tileDT, xVar = "dateTimeUTC", yVar = "site", fillVar = "value", yLab = yLab)

p + scale_x_datetime(date_breaks = "7 day", date_labels = "%a %d %b") + theme(axis.text.x = element_text(angle = 90, 
    hjust = 1))
Availability and level of PM 10 data over time

Figure 11.5: Availability and level of PM 10 data over time

tileDT <- pm10dt[dateTimeUTC > as.Date("2020-02-01")]
p <- makeTilePlot(tileDT, xVar = "dateTimeUTC", yVar = "site", fillVar = "value", yLab = yLab)

p + scale_x_datetime(date_breaks = "7 day", date_labels = "%a %d %b") + theme(axis.text.x = element_text(angle = 90, 
    hjust = 1))
Availability and level of PM 10 data over time

Figure 11.6: Availability and level of PM 10 data over time

12 Runtime

Report generated using knitr in RStudio with R version 3.6.3 (2020-02-29) running on x86_64-apple-darwin15.6.0 (Darwin Kernel Version 19.4.0: Wed Mar 4 22:28:40 PST 2020; root:xnu-6153.101.6~15/RELEASE_X86_64).

t <- proc.time() - myParams$startTime

elapsed <- t[[3]]

Analysis completed in 40.014 seconds ( 0.67 minutes).

R packages used:

  • data.table - (Dowle et al. 2015)
  • ggplot2 - (Wickham 2009)
  • here - (Müller 2017)
  • kableExtra - (Zhu 2018)
  • lubridate - (Grolemund and Wickham 2011)
  • skimr - (Arino de la Rubia et al. 2017)
  • viridis - (Garnier 2018)

References

Arino de la Rubia, Eduardo, Hao Zhu, Shannon Ellis, Elin Waring, and Michael Quinn. 2017. Skimr: Skimr. https://github.com/ropenscilabs/skimr.

Dowle, M, A Srinivasan, T Short, S Lianoglou with contributions from R Saporta, and E Antonyan. 2015. Data.table: Extension of Data.frame. https://CRAN.R-project.org/package=data.table.

Garnier, Simon. 2018. Viridis: Default Color Maps from ’Matplotlib’. https://CRAN.R-project.org/package=viridis.

Grolemund, Garrett, and Hadley Wickham. 2011. “Dates and Times Made Easy with lubridate.” Journal of Statistical Software 40 (3): 1–25. http://www.jstatsoft.org/v40/i03/.

Müller, Kirill. 2017. Here: A Simpler Way to Find Your Files. https://CRAN.R-project.org/package=here.

Wickham, Hadley. 2009. Ggplot2: Elegant Graphics for Data Analysis. Springer-Verlag New York. http://ggplot2.org.

Zhu, Hao. 2018. KableExtra: Construct Complex Table with ’Kable’ and Pipe Syntax. https://CRAN.R-project.org/package=kableExtra.